/*
 * CHttpServer.cpp
 *
 *  Created on: 2016-7-25
 *      Author: terry
 */

#include "CHttpServer.h"
#include "TStringUtil.h"
#include "TStringCast.h"
#include <iostream>
#include <fstream>
#include "TFileUtil.h"
#include "CLog.h"
#include "HttpUtil.h"

#define USER_DIGEST_FILE "user.htpasswd"
#define HTTP_DOMAIN		 "HttpServer"

static void ev_null_handler(struct mg_connection *, int ev, void *)
{
	// pass
}

static bool mg_startswith(const struct mg_str& src, const struct mg_str& sub)
{
	if (src.len < sub.len)
	{
		return false;
	}

	for (size_t i = 0; i < sub.len; i++)
	{
		if (src.p[i] != sub.p[i])
		{
			return false;
		}
	}
	return true;
}

static bool is_websocket(const struct mg_connection *nc)
{
	return (nc->flags & MG_F_IS_WEBSOCKET) != 0;
}


static void ev_handler(struct mg_connection *nc, int ev, void *ev_data)
{
	CHttpServer* pThis = (CHttpServer*)nc->mgr->user_data;
	if (pThis == NULL)
	{
		pThis = (CHttpServer*)nc->user_data;
	}

	//CLog::debug("ev_handler. thread:%d, pThis:%p, ev:%d\n",
	//	GetCurrentThreadId(),
	//	pThis, ev);

	if (ev == MG_EV_POLL)
	{
		pThis->handleAsyncResult(nc, ev, ev_data);
	}
	else if (ev == MG_EV_HTTP_REQUEST)
	{
		struct http_message *hm = (struct http_message *) ev_data;
		pThis->handleHttpEvent(nc, ev, hm);
	}
    else if (ev == MG_EV_TIMER)
    {
        //
    }
    else if (ev == MG_EV_SEND)
    {
        //
    }
    else if (ev == MG_EV_WEBSOCKET_HANDSHAKE_REQUEST)
    {
    	struct http_message *hm = (struct http_message *) ev_data;

    	static const struct mg_str ws_prefix = mg_mk_str("/api/ws");
    	if (mg_startswith(hm->uri, ws_prefix))
    	{
    		/// accept
    	}
    	else
    	{
    		/// reject
    		nc->flags |= MG_F_CLOSE_IMMEDIATELY;
    	}
    }
    else if (ev == MG_EV_WEBSOCKET_HANDSHAKE_DONE)
    {
    	//
    }
    else if (ev == MG_EV_WEBSOCKET_FRAME)
    {
		struct websocket_message *wm = (struct websocket_message *) ev_data;
		pThis->handleWebSocketFrame(nc, ev, wm);
    }

}

struct file_writer_data 
{
  FILE *fp;
  size_t bytes_written;
  std::string filepath;
  std::string origin;

  file_writer_data():
	  fp(),
	  bytes_written()
  {
  }
};

static void handle_upload(struct mg_connection *nc, int ev, void *ev_data)
{
	CHttpServer* pThis = (CHttpServer*)nc->mgr->user_data;
	pThis->handleUpload(nc, ev, ev_data);

}

static void handle_direntry(struct mg_connection *nc, int ev, void *ev_data)
{
	CHttpServer* pThis = (CHttpServer*)nc->mgr->user_data;
	pThis->handleDirEntry(nc, ev, ev_data);
}


struct ValueName
{
    int value;
    const char* name;
};

static ValueName s_codeNames[] =
{
    {200, "OK"},
    {400, "Bad Request"},
    {401, "Unauthorized"},
    {402, "Payment Required"},
    {403, "Forbidden   "},
    {404, "Not Found   "},
    {405, "Method Not Allowed"},
    {406, "Not Acceptable"},
    {407, "Proxy Authentication Required"},
    {408, "Request Timeout"},
    {410, "Gone        "},
    {411, "Length Required"},
    {412, "Precondition Failed"},
    {413, "Request Entity Too Large"},
    {414, "Request-URI Too Long"},
    {415, "Unsupported Media Type"},
    {451, "Invalid parameter"},
    {452, "Illegal Conference Identifier"},
    {453, "Not Enough Bandwidth"},
    {454, "Session Not Found"},
    {455, "Method Not Valid In This State"},
    {456, "Header Field Not Valid"},
    {457, "Invalid Range   PLAY"},
    {458, "Parameter Is Read-Only"},
    {459, "Aggregate Operation Not Allowed"},
    {460, "Only Aggregate Operation Allowed"},
    {461, "Unsupported Transport"},
    {462, "Destination Unreachable"},
    {500, "Internal Server Error"},
    {501, "Not Implemented"},
    {502, "Bad Gateway "},
    {503, "Service Unavailable"},
    {504, "Gateway Timeout"},
    {505, "RTSP Version Not Supported"},
    {551, "Option not support"},
};

static const char* getCodeName(int code)
{
    for (size_t i = 0; i < sizeof(s_codeNames)/sizeof(s_codeNames[0]); i ++)
    {
        if (code == s_codeNames[i].value)
        {
            return s_codeNames[i].name;
        }
    }
    return "";
}



CHttpServer::CHttpServer():
	m_handler(),
	m_mgr(),
	m_opts(),
	m_connection(),
	m_frameSeq()
{
	mg_mgr_init(&m_mgr, this);

	m_webRoot = ".";
	m_opts.document_root = m_webRoot.c_str();

	m_userFile = USER_DIGEST_FILE;
	m_opts.auth_domain = HTTP_DOMAIN;
	m_opts.global_auth_file = m_userFile.c_str();
	
	m_uploadDir = ".";
	

	setupUser();
}

CHttpServer::~CHttpServer()
{
	stop();
}

bool CHttpServer::setWebRoot(const char* dirPath)
{
	if (!dirPath || strlen(dirPath) <= 0)
	{
		return false;
	}

	m_webRoot = dirPath;

	m_opts.document_root = m_webRoot.c_str();

	return true;
}

bool CHttpServer::setUploadDir(const char* dirPath)
{
	if (!dirPath || strlen(dirPath) <= 0)
	{
		return false;
	}

	m_uploadDir = dirPath;

	return true;
}

bool CHttpServer::setUserFile(const char* filePath)
{
	if (!filePath || strlen(filePath) <= 0)
	{
		m_userFile.clear();
		m_opts.global_auth_file = NULL;
	}
	else
	{
		m_userFile = filePath;
		m_opts.global_auth_file = m_userFile.c_str();
	}

	return true;
}

bool CHttpServer::addDir(const char* url, const char* dirPath)
{
	if (strlen(url) == 0)
	{
		return false;
	}

	if (strlen(dirPath) == 0)
	{
		return false;
	}

	DirEntry entry;
	if (findDirEntry(url, entry))
	{
		return false;
	}

	entry.dirPath = dirPath;
	entry.url = url;
	entry.opts.document_root = entry.dirPath.c_str();

	m_entryMap[url] = entry;

	return true;
}

bool CHttpServer::start(int port, HttpHandler* handler)
{
	if (port < 0 || !handler)
	{
		return false;
	}

	m_handler = handler;
	m_port = comn::StringCast::toString(port);

	m_connection = mg_bind(&m_mgr, m_port.c_str(), ev_handler);
	if (!m_connection)
	{
		return false;
	}

	m_connection->user_data = this;
	//mg_enable_multithreading(m_connection);

	mg_set_protocol_http_websocket(m_connection);

	mg_register_http_endpoint(m_connection, "/upload", handle_upload);


	DirEntryMap::iterator it = m_entryMap.begin();
	for (; it != m_entryMap.end(); ++ it)
	{
		mg_register_http_endpoint(m_connection, it->first.c_str(), handle_direntry);
	}

	return comn::Thread::start();
}

void CHttpServer::stop()
{
	if (isRunning())
	{
		comn::Thread::stop();
	}

	mg_mgr_free(&m_mgr);

	{
		comn::AutoCritSec lock(m_cs);
		m_frameDeque.clear();
	}
}

bool CHttpServer::isStarted()
{
	return comn::Thread::isRunning();
}

void CHttpServer::handleHttpEvent(struct mg_connection *nc, int ev, struct http_message *hm)
{
	static const struct mg_str sys_prefix = mg_mk_str("/api/sys");
	static const struct mg_str file_prefix = mg_mk_str("/api/file");

	if (HttpUtil::isOption(hm->method))
	{
		sendJsonResponse(nc, 200, NULL, "");
		return;
	}

	std::string prefix;
	if (findPrefix(hm->uri, prefix))
	{
		handleJsonApi(nc, hm, hm->uri);
	}
	else if (mg_startswith(hm->uri, sys_prefix))
	{
		mg_str uri;
		uri.p = hm->uri.p + (sys_prefix.len - 1);
		uri.len = hm->uri.len - (sys_prefix.len - 1);

		handleSysApi(nc, hm, uri);
	}
	else if (mg_startswith(hm->uri, file_prefix))
	{
		mg_str uri;
		uri.p = hm->uri.p + (sys_prefix.len - 1);
		uri.len = hm->uri.len - (sys_prefix.len - 1);

		handleFileApi(nc, hm, uri);
	}
	else
	{
		mg_serve_http(nc, hm, m_opts);
	}
}

void CHttpServer::handleWebSocketFrame(struct mg_connection *nc, int ev, struct websocket_message* wm)
{
	m_handler->handleWebSocketFrame(wm->data, wm->size, wm->flags);
}


int CHttpServer::run()
{
	while (!m_canExit)
	{
		mg_mgr_poll(&m_mgr, 10000);

		clearExpiredAsyncResult();
	}
	return 0;
}

void CHttpServer::doStop()
{
	int ctrl = 0;
	mg_broadcast(&m_mgr, ev_null_handler, &ctrl, sizeof(ctrl));
}

void CHttpServer::handleJsonApi(struct mg_connection* nc, struct http_message *hm, mg_str& uri)
{
	int code = 404;
    std::string json = "";
	if (m_handle)
	{
		HttpRequest request;
		request.server = this;
		request.connection = nc;
		request.message = hm;
		request.method = hm->method;
		request.uri = uri;
		request.query = hm->query_string;
		request.body = hm->body;
		int ret = m_handler->handleJsonApi(request, json);
		if (ret == 0)
		{
			code = 200;
		}
		else if (ret == HttpHandler::ASYNC_RESULT)
		{
			return;
		}
		else if (ret < 100 || ret > 600)
		{
			code = 400;
		}
	}

	sendJsonResponse(nc, code, NULL, json);
}

void CHttpServer::sendJsonResponse(mg_connection *nc, int code, const char *reason, const std::string& json)
{
	sendResponse(nc, code, reason, "*", "application/json", json);
}

void CHttpServer::sendJsonResponse(mg_connection *nc, int code, const char *reason, const char* json)
{
	sendResponse(nc, code, reason, "*", "application/json", json);
}

void CHttpServer::sendResponse(mg_connection *nc, int code, const char *reason,
	const std::string& origin,
	const std::string& mime,
	const std::string& content)
{
	if (reason == NULL)
	{
		reason = getCodeName(code);
	}

	mg_printf(nc, "HTTP/1.1 %d %s\r\n"
		"Cache-Control: no-cache\r\n"
		"Content-Type: %s\r\n"
		"Access-Control-Allow-Origin: %s\r\n"
		"Access-Control-Allow-Headers: Origin, X-Requested-With, Content-Type, Accept\r\n"
		"Access-Control-Allow-Methods: POST, GET, OPTIONS, DELETE, PUT\r\n"
		"Access-Control-Allow-Credentials: true\r\n"
		"Content-Length: %d\r\n\r\n%s",
		code, reason,
		mime.c_str(),
		origin.c_str(),
		(int)content.size(), content.c_str());
}

void CHttpServer::sendResponse(mg_connection *nc, int code, const char *reason,
	const std::string& origin,
	const std::string& mime,
	const char* content)
{
	if (reason == NULL)
	{
		reason = getCodeName(code);
	}

	int size = strlen(content);

	mg_printf(nc, "HTTP/1.1 %d %s\r\n"
		"Cache-Control: no-cache\r\n"
		"Content-Type: %s\r\n"
		"Access-Control-Allow-Origin: %s\r\n"
		"Access-Control-Allow-Headers: Origin, X-Requested-With, Content-Type, Accept\r\n"
		"Access-Control-Allow-Methods: POST, GET, OPTIONS, DELETE, PUT\r\n"
		"Access-Control-Allow-Credentials: true\r\n"
		"Content-Length: %d\r\n\r\n%s",
		code, reason,
		mime.c_str(),
		origin.c_str(),
		size, content);
}

void CHttpServer::handleFileApi(struct mg_connection* nc, struct http_message *hm, mg_str& uri)
{
	int code = 404;
    std::string resp = "";
    std::string mime = "application/octet-stream";

	if (m_handle)
	{
		HttpRequest request;
		request.server = this;
		request.connection = nc;
		request.message = hm;
		request.method = hm->method;
		request.uri = uri;
		request.query = hm->query_string;
		request.body = hm->body;

		int ret = m_handler->handleFileApi(request, resp, mime);
		if (ret == 0)
		{
			code = 200;
		}
		else if (ret < 100)
		{
			code = ret + 500;
		}
		else if (ret < 0)
		{
			code = 500;
		}
	}

	const char* reason = getCodeName(code);
	mg_printf(nc,   "HTTP/1.1 %d %s\r\n"
					"Cache-Control: no-cache\r\n"
					"Content-Type: %s\r\n"
					"Access-Control-Allow-Origin: *\r\n"
					"Content-Length: %d\r\n\r\n%s",
					code, reason,
					mime.c_str(),
					(int)resp.size(), resp.c_str());
}

void CHttpServer::handleUpload(struct mg_connection *nc, int ev, void *ev_data)
{
	struct file_writer_data * fileData  = (struct file_writer_data *) nc->user_data;
	struct mg_http_multipart_part *mp = (struct mg_http_multipart_part*)ev_data;

    if (ev == MG_EV_HTTP_PART_BEGIN)
    {
    	if (fileData)
    	{
			fileData->filepath = makeUploadPath(mp->file_name);
			fileData->fp = fopen(fileData->filepath.c_str(), "wb");
			fileData->bytes_written = 0;

            if (fileData->fp == NULL)
            {
            	mg_printf(nc, 
                        "HTTP/1.1 500 Failed to open a file\r\n"
						"Access-Control-Allow-Origin: %s\r\n"
						"Access-Control-Allow-Headers: Origin, X-Requested-With, Content-Type, Accept\r\n"
						"Access-Control-Allow-Methods: POST, GET, OPTIONS, DELETE, PUT\r\n"
						"Access-Control-Allow-Credentials: true\r\n"
                        "Content-Length: 0\r\n\r\n",
						fileData->origin.c_str());
            	nc->flags |= MG_F_SEND_AND_CLOSE;
				
				delete fileData;
				fileData = NULL;

            	return;
            }
    	}
    }
	else if (ev == MG_EV_HTTP_PART_DATA)
	{
		if (fileData)
		{
			if (fwrite(mp->data.p, 1, mp->data.len, fileData->fp) != mp->data.len)
			{
				mg_printf(nc, 
					"HTTP/1.1 500 Failed to write to a file\r\n"
					"Access-Control-Allow-Origin: %s\r\n"
					"Access-Control-Allow-Headers: Origin, X-Requested-With, Content-Type, Accept\r\n"
					"Access-Control-Allow-Methods: POST, GET, OPTIONS, DELETE, PUT\r\n"
					"Access-Control-Allow-Credentials: true\r\n"
					"Content-Length: 0\r\n\r\n",
					fileData->origin.c_str());
				nc->flags |= MG_F_SEND_AND_CLOSE;
				return;
			}
			fileData->bytes_written += mp->data.len;
		}
    }
    else if (ev == MG_EV_HTTP_PART_END)
    {
		if (fileData)
		{
    		mg_printf(nc,
					"HTTP/1.1 200 OK\r\n"
					"Content-Type: text/plain\r\n"
					"Access-Control-Allow-Origin: %s\r\n"
					"Access-Control-Allow-Headers: Origin, X-Requested-With, Content-Type, Accept\r\n"
					"Access-Control-Allow-Methods: POST, GET, OPTIONS, DELETE, PUT\r\n"
					"Access-Control-Allow-Credentials: true\r\n"
					"Connection: close\r\n\r\n"
					"Written %ld of POST data to file:%s\n\n",
					fileData->origin.c_str(),
					(long) ftell(fileData->fp),
					fileData->filepath.c_str());
    		nc->flags |= MG_F_SEND_AND_CLOSE;
    		fclose(fileData->fp);

    		std::string filepath(fileData->filepath);

    		delete fileData;
    		nc->user_data = NULL;

    		if (m_handler)
    		{
    			m_handler->handleUpload(mp->var_name, filepath);
    		}
		}
    }
    else if (ev == MG_EV_HTTP_MULTIPART_REQUEST)
    {
		struct http_message *hm = (struct http_message *) ev_data;
		std::string origin("*");
		struct mg_str *origin_hdr = mg_get_http_header(hm, "Origin");
		if (origin_hdr)
		{
			origin.assign(origin_hdr->p, origin_hdr->len);
		}

		fileData = new file_writer_data();
		fileData->origin = origin;
		nc->user_data = fileData;
    }
	else if (ev == MG_EV_HTTP_REQUEST)
	{
		struct http_message *hm = (struct http_message *) ev_data;
		if (mg_vcmp(&hm->method, "OPTIONS") == 0)
		{
			
		}
		else
		{
			//
		}

		std::string origin("*");
		struct mg_str *origin_hdr = mg_get_http_header(hm, "Origin");
		if (origin_hdr)
		{
			origin.assign(origin_hdr->p, origin_hdr->len);
		}

		sendResponse(nc, 200, NULL, origin, "application/octet-stream", std::string());
	}
	
}

std::string	CHttpServer::makeUploadPath(const char* filename)
{
	std::string fullpath = m_uploadDir;
	fullpath += '/';
	fullpath += filename;
	return fullpath;
}

bool CHttpServer::setupUser()
{
	FILE* f = fopen(m_userFile.c_str(), "rb");
	if (f)
	{
		fclose(f);
		return true;
	}

	return resetUser("admin", "admin");
}

std::string CHttpServer::makeUser(const std::string& name, const std::string& realm, const std::string& passwd)
{
	char buf[33] = {0};
	std::string src = name + ":" + realm + ":" + passwd;
	cs_md5(buf, src.c_str(), src.size(), NULL);
	return buf;
}

bool CHttpServer::resetUser(const std::string& name, const std::string& passwd)
{
	bool done = false;
	FILE* f = fopen(m_userFile.c_str(), "wb");
	if (f)
	{
		std::string md5 = makeUser(name, HTTP_DOMAIN, passwd);
		std::string line(name);
		line += ":";
		line += HTTP_DOMAIN;
		line += ":";
		line += md5;
		fwrite(line.c_str(), 1, line.size(), f);
		fclose(f);
		done = true;
	}
	return done;
}

void CHttpServer::handleSysApi(struct mg_connection* nc, struct http_message *hm, mg_str& uri)
{
	int code = 404;
	std::string json = "{}";

	static const struct mg_str password_prefix = mg_mk_str("/password");
	if (mg_vcmp(&uri, password_prefix.p) == 0)
	{
		char name[256] = {0};
		char password[256] = {0};
		mg_get_http_var(&hm->body, "user", name, sizeof(name));
		mg_get_http_var(&hm->body, "password", password, sizeof(password));

		resetUser(name, password);

		code = 200;
	}

	sendJsonResponse(nc, code, NULL, json);
}

bool CHttpServer::findDirEntry(const char* url, DirEntry& entry)
{
	bool found = false;
	DirEntryMap::const_iterator it = m_entryMap.find(url);
	if (it != m_entryMap.end())
	{
		entry = it->second;
		found = true;
	}
	return found;
}

bool CHttpServer::findDirEntry(struct mg_str& url, DirEntry& entry)
{
	bool found = false;
	DirEntryMap::const_iterator it = m_entryMap.begin();
	for (; it != m_entryMap.end(); ++ it)
	{
		mg_str prefix = mg_mk_str(it->first.c_str());
		if (mg_startswith(url, prefix))
		{
			entry = it->second;
			found = true;
		}
	}
	return found;
}

void CHttpServer::handleDirEntry(struct mg_connection *nc, int ev, void *ev_data)
{
	struct http_message *hm = (struct http_message *)ev_data;

	DirEntry entry;
	if (findDirEntry(hm->uri, entry))
	{
		entry.opts.document_root = entry.dirPath.c_str();
		hm->uri.p = hm->uri.p + entry.url.size();
		hm->uri.len = hm->uri.len - entry.url.size();
		mg_serve_http(nc, hm, entry.opts);
	}
	else
	{
		mg_printf(nc, "%s",
				"HTTP/1.1 404 Not found\r\n"
		        "Content-Length: 0\r\n\r\n");
	}
}

static void ev_broadcast_handler(struct mg_connection * nc, int ev, void * ev_data)
{
	if (is_websocket(nc))
	{
		ctl_msg* msg = (ctl_msg*)ev_data;
		mg_send_websocket_frame(nc, WEBSOCKET_OP_TEXT, msg->message, msg->len);
	}
}

void CHttpServer::broadcast(void *data, size_t len)
{
	mg_broadcast(&m_mgr, ev_broadcast_handler, data, len);
}

bool CHttpServer::addJsonApi(const char* prefix)
{
	if ((prefix == NULL) || (strlen(prefix) == 0))
	{
		return false;
	}

	m_prefixMap[prefix] = prefix;

	return true;
}

bool CHttpServer::putAsyncResult(HttpAsyncResult& result)
{
	if (!result.connection)
	{
		return false;
	}

	{
		comn::AutoCritSec lock(m_cs);
		AsyncEntry entry;
		entry.result = result;
		entry.expired = time(NULL) + ASYNC_DURATION;
		m_asyncResultMap[result.connection] = entry;
	}

	signalAsyncResult();

	return true;
}

bool CHttpServer::findPrefix(const struct mg_str& url, std::string& prefix)
{
	bool found = false;
	PrefixMap::const_iterator it = m_prefixMap.begin();
	for (; it != m_prefixMap.end(); ++it)
	{
		struct mg_str api_prefix = mg_mk_str(it->first.c_str());
		if (mg_startswith(url, api_prefix))
		{
			prefix = it->first;
			found = true;
			break;
		}
	}
	return found;
}

void CHttpServer::clearExpiredAsyncResult()
{
	comn::AutoCritSec lock(m_cs);
	time_t curTime = time(NULL);
	AsyncResultMap::iterator it = m_asyncResultMap.begin();
	while (it != m_asyncResultMap.end())
	{
		if (it->second.expired > curTime)
		{
			m_asyncResultMap.erase(it++);
		}
		else
		{
			it++;
		}
	}
}

bool CHttpServer::fetchAsyncResult(mg_connection* connection, HttpAsyncResult& result)
{
	bool found = false;
	comn::AutoCritSec lock(m_cs);
	AsyncResultMap::iterator it = m_asyncResultMap.find(connection);
	if (it != m_asyncResultMap.end())
	{
		result = it->second.result;
		m_asyncResultMap.erase(it);
		found = true;
	}
	return found;
}

void CHttpServer::signalAsyncResult()
{
	int ctrl = 0;
	mg_broadcast(&m_mgr, ev_null_handler, &ctrl, sizeof(ctrl));
}

static void ev_send_response_handler(struct mg_connection * nc, int ev, void * ev_data)
{
	ctl_msg* msg = (ctl_msg*)ev_data;
	if (nc == NULL || nc == msg->nc)
	{
		msg->message[msg->len] = 0;
		CHttpServer::sendJsonResponse(nc, msg->tag, NULL, msg->message);
	}
}

bool CHttpServer::sendResponse(mg_connection* nc, int code, const std::string& content)
{
	if (content.size() >= MG_CTL_MSG_MESSAGE_SIZE)
	{
		HttpAsyncResult result(nc);
		result.code = code;
		result.content = content;
		return putAsyncResult(result);
	}

	void* data = const_cast<char*>(content.c_str());
	mg_cast(&m_mgr, nc, ev_send_response_handler, code, data, content.length());
	return true;
}

static void ev_send_handler(struct mg_connection * nc, int ev, void * ev_data)
{
	ctl_msg* msg = (ctl_msg*)ev_data;
	if (nc == NULL || nc == msg->nc)
	{
		mg_send(nc, msg->message, msg->len);
	}
}

bool CHttpServer::send(mg_connection* nc, const void* data, size_t len)
{
	mg_cast(&m_mgr, nc, ev_send_handler, 0, const_cast<void*>(data), len);
	return true;
}


static void ev_broadcast_bin_handler(struct mg_connection * nc, int ev, void * ev_data)
{
	if (is_websocket(nc))
	{
		CHttpServer* pThis = (CHttpServer*)nc->mgr->user_data;
		ctl_msg* msg = (ctl_msg*)ev_data;
		int seq = *((int*)msg->message);
		CHttpServer::Frame frame;
		if (pThis->fetchFrame(seq, frame))
		{
			mg_send_websocket_frame(nc, WEBSOCKET_OP_BINARY, frame.data.c_str(), frame.data.size());
		}
	}
}

void CHttpServer::broadcastBinary(void *data, size_t len)
{
	int seq = 0;
	{
		std::string str((char*)data, len);

		comn::AutoCritSec lock(m_cs);
		seq = m_frameSeq++;
		Frame frame(seq, str);
		m_frameDeque.push_back(frame);
	}

	mg_broadcast(&m_mgr, ev_broadcast_bin_handler, &seq, sizeof(seq));
}


void CHttpServer::handleAsyncResult(struct mg_connection *nc, int ev, void *ev_data)
{
	HttpAsyncResult result;
	if (fetchAsyncResult(nc, result))
	{
		CLog::debug("got async result. %p\n", nc);
		sendJsonResponse(nc, result.code, NULL, result.content);
	}
}

bool CHttpServer::fetchFrame(int seq, Frame& frame)
{
	comn::AutoCritSec lock(m_cs);
	FrameDeque::iterator itBegin = m_frameDeque.begin();
	FrameDeque::iterator it = m_frameDeque.begin();
	for (; it != m_frameDeque.end(); ++it)
	{
		if (it->seq == seq)
		{
			frame = *it;

			if (it != itBegin)
			{
				m_frameDeque.erase(itBegin, it);
			}
			
			return true;
		}
	}
	return false;
}